package com.abapi.pay.client;

import com.abapi.pay.AbapiPayProperties;
import com.abapi.pay.handler.ClientHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Author ldx
 * @Date 2019/10/30 15:30
 * @Description
 * @Version 1.0.0
 */
public class PayTcpClient {

    Logger logger = LoggerFactory.getLogger(getClass());

    private NioEventLoopGroup workGroup = new NioEventLoopGroup(4);
    private Channel channel;
    private Bootstrap bootstrap;

    private AbapiPayProperties abapiPayProperties;

    public PayTcpClient(AbapiPayProperties abapiPayProperties){
        this.abapiPayProperties = abapiPayProperties;
    }

    public void start() {
        try {
            bootstrap = new Bootstrap();
            bootstrap.group(workGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        protected void initChannel(SocketChannel socketChannel){
                            ChannelPipeline p = socketChannel.pipeline();
                            p.addLast(new IdleStateHandler(0, 0, 0));
                            p.addLast(new DelimiterBasedFrameDecoder(3072, Unpooled.copiedBuffer(new byte[] { 0x7e }),
                                    Unpooled.copiedBuffer(new byte[] { 0x7e, 0x7e })));
                            p.addLast(new ClientHandler(PayTcpClient.this,abapiPayProperties));
                        }
                    });
            doConnect();

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void doConnect() {
        if (channel != null && channel.isActive()) {
            return;
        }


        String [] clusters = abapiPayProperties.getAdminHost().split(",");
        Arrays.stream(clusters).forEach(e->{
            String host = e.split(":")[0];
            Integer port = Integer.parseInt(e.split(":")[1]);
            //ChannelFuture future = bootstrap.connect(host, port);
            bootstrap.remoteAddress(host,port);
            //异步连接tcp服务端
            ChannelFuture future = bootstrap.connect().addListener((ChannelFuture futureListener) -> {
                final EventLoop eventLoop = futureListener.channel().eventLoop();
                if (!futureListener.isSuccess()) {
                    //服务器未启动 连接tcp服务器不成功
                    logger.debug(host+":"+port+"第一次连接与支付PAY-ADMIN断开连接!在10s之后准备尝试重连!");
                    //10秒后重连
                    eventLoop.schedule(() -> doConnect(bootstrap, host, port), 10, TimeUnit.SECONDS);
                }
            });

            /*future.addListener(new ChannelFutureListener() {
                public void operationComplete(ChannelFuture futureListener) throws Exception {
                    if (futureListener.isSuccess()) {
                        channel = futureListener.channel();
                        System.out.println("Connect to server successfully!");
                    } else {
                        System.out.println("Failed to connect to server, try connect after 10s");

                        futureListener.channel().eventLoop().schedule(new Runnable() {
                            @Override
                            public void run() {
                                doConnect();
                            }
                        }, 10, TimeUnit.SECONDS);
                    }
                }
            });*/
        });

    }

    /**
     * 重新连接tcp服务端
     */
    public void doConnect(Bootstrap bootstrap , String host, int port) {
        try {
            if (bootstrap != null) {
                bootstrap.remoteAddress(host, port);
                ChannelFuture f = bootstrap.connect().addListener((ChannelFuture futureListener) -> {
                    final EventLoop eventLoop = futureListener.channel().eventLoop();
                    if (!futureListener.isSuccess()) {
                        //连接tcp服务器不成功 10后重连
                        logger.debug(host+":"+port+"重试连接与支付PAY-ADMIN断开连接!在10s之后准备尝试重连!");
                        eventLoop.schedule(() -> doConnect(bootstrap, host, port), 10, TimeUnit.SECONDS);
                    }
                });
                //channels.put(port, f);
            }
        } catch (Exception e) {
            logger.error(host+":"+port+"客户端连接失败!",e);
        }

    }

}
